package com.vdimitr.happycube.puzzle.solution;

import com.vdimitr.happycube.puzzle.piece.PuzzlePiece;
import com.vdimitr.happycube.puzzle.piece.PuzzlePieceConstants;

/**
 * This class is responsible for holding a possible solution to a cube
 * consisting of 6 puzzle pieces. Internally, it uses a 4x3 matrix to place the
 * puzzle pieces as the solution is constructed step by step. The puzzle pieces
 * are added by filling the middle column from top to bottom, then the top left
 * corner and finally the top right corner.The final solution matrix should look
 * like this: </br></br> 
 * |p5|p1|p6| </br> 
 * |p0|p2|p0| </br> 
 * |p0|p3|p0| </br>
 * |p0|p4|p0| </br>
 * </br> 
 * Where
 * <ul>
 * <li>p is a puzzle piece
 * <li>1 to 6 are the positions to place the puzzle pieces
 * <li>and p0 stands for a null puzzle piece
 * </ul>
 * N.B. The matrix was selected to be like this because it helps while 
 * exporting the solution to output.
 * 
 * @author vasileios.dimitriadis
 */
public class PuzzlePieceSolution {

	/**
	 * The matrix holding the puzzle pieces that form a solution
	 */
	private PuzzlePiece[][] matrix = new PuzzlePiece[4][3];

	/**
	 * Get the matrix representation of the cube solution
	 * 
	 * @return the matrix containing the puzzle pieces that form a cube
	 */
	public PuzzlePiece[][] getMatrix() {
		return matrix;
	}

	/** 
	 * Default constructor
	 */
	public PuzzlePieceSolution() {
	}
	
	/**
	 * Places a puzzle piece at a corresponding position. Should be called after
	 * making sure that the puzzle piece is part of the solution
	 * 
	 * @param puzzlePiece
	 *            a puzzle piece which is part of the solution
	 * @param position
	 *            Should be between 1 and 6 (inclusive)
	 */
	public void setPuzzlePieceAtPosition(PuzzlePiece puzzlePiece, int position) {
		switch (position) {
		case 1:
			this.matrix[0][1] = puzzlePiece;
			break;
		case 2:
			this.matrix[1][1] = puzzlePiece;
			break;
		case 3:
			this.matrix[2][1] = puzzlePiece;
			break;
		case 4:
			this.matrix[3][1] = puzzlePiece;
			break;
		case 5:
			this.matrix[0][0] = puzzlePiece;
			break;
		case 6:
			this.matrix[0][2] = puzzlePiece;
			break;
		default:
			throw new IllegalArgumentException("Position: " + position
					+ " is not valid");
		}
	}

	/**
	 * Convenient method for accessing a puzzle piece at a specific position of
	 * the solution in order to be used for puzzle piece matching purposes
	 * 
	 * @param position
	 *            Should be between 1 and 6 (inclusive)
	 * @return the puzzle piece at the specified position
	 */
	public PuzzlePiece getPuzzlePieceAtPosition(int position) {
		switch (position) {
		case 1:
			return this.matrix[0][1];
		case 2:
			return this.matrix[1][1];
		case 3:
			return this.matrix[2][1];
		case 4:
			return this.matrix[3][1];
		case 5:
			return this.matrix[0][0];
		case 6:
			return this.matrix[0][2];
		default:
			throw new IllegalArgumentException("Position: " + position
					+ " is not valid");
		}
	}

	/**
	 * Convenient method for accessing the middle elements of the solution in
	 * order to be used for puzzle piece matching purposes
	 * 
	 * @return the puzzle pieces of the middle column
	 */
	public PuzzlePiece[] getMiddlePuzzlePieceColumn() {
		PuzzlePiece[] middleColumn = new PuzzlePiece[4];
		for (int i = 0; i < middleColumn.length; i++) {
			middleColumn[i] = this.matrix[i][1];
		}
		return middleColumn;
	}

	/**
	 * Checks if the solution contains all the puzzle pieces needed
	 * 
	 * @return true if all positions have been covered by puzzle pieces
	 */
	public boolean isComplete() {
		for (int puzzlePiecePositionCount = 1; 
				puzzlePiecePositionCount <= PuzzlePieceConstants.NUMBER_OF_PUZZLEPIECES; 
				puzzlePiecePositionCount++) {
			if (getPuzzlePieceAtPosition(puzzlePiecePositionCount) == null) {
				return false;
			}
		}
		return true;
	}
}
